using OAuthTools = Dimebrain.TweetSharp.Core.OAuth.OAuthTools;
using WebParameterCollection = Dimebrain.TweetSharp.Core.Web.WebParameterCollection;
using WebParameter = Dimebrain.TweetSharp.Core.Web.WebParameter;
using Dimebrain.TweetSharp.Extensions;
using Dimebrain.TweetSharp.Fluent;
using Log = Microsoft.Samples.ServiceHosting.HelloFabric.ApplicationEnvironment;

namespace KlearTweet.Web.Storage
{
    public static class AccountsManager
    {
        public static readonly string ConsumerKey;
        public static readonly string ConsumerSecret;

        static AccountsManager()
        {
            ConsumerKey = Microsoft.ServiceHosting.ServiceRuntime.RoleManager.GetConfigurationSetting("TwitterAuthConsumerKey");
            ConsumerSecret = Microsoft.ServiceHosting.ServiceRuntime.RoleManager.GetConfigurationSetting("TwitterAuthConsumerSecret");
        }

        public static string GetAuthorization(string clientAuth, bool isPost, string url, string body)
        {
            const string prefixAuthToken = "oauth_token=\"";
            var index = clientAuth.IndexOf(prefixAuthToken);
            if (index > 0)
            {
                var clientAuthToken = clientAuth.Substring(index + prefixAuthToken.Length);
                index = clientAuthToken.IndexOf("\"");
                if (index > 0)
                {
                    clientAuthToken = clientAuthToken.Substring(0, index);
                    var account = KlearStorage.Persister.Instance.SelectByPartitionKey<Account>(clientAuthToken);
                    if (account != null)
                        return account.GetOAuthAuthentication(isPost, url, body);
                }
            }

            return clientAuth; // Authentication not recognized, keep it as-is
        }


        public static string GetOAuthAuthentication(this Account account, bool isPost, string url, string body)
        {
            var timestamp = OAuthTools.GetTimestamp();
            var nonce = OAuthTools.GetNonce();

            var authParameters = new WebParameterCollection
            {
                new WebParameter("oauth_consumer_key", ConsumerKey),
                new WebParameter("oauth_token", account.AuthToken),
                new WebParameter("oauth_nonce", nonce),
                new WebParameter("oauth_timestamp", timestamp),
                new WebParameter("oauth_signature_method", "HMAC-SHA1"),
                new WebParameter("oauth_version", "1.0"),
            };

            var allParameters = new WebParameterCollection(authParameters);
            if (!string.IsNullOrEmpty(body))
            {
                var bodyParams = body.Split('&'); // TODO: Assert that this is always "well-formed"
                foreach (var bodyParam in bodyParams)
                {
                    var parts = bodyParam.Split('=');
                    allParameters.Add(parts[0], System.Web.HttpUtility.UrlDecode(parts[1]));
                }
            }

            var uri = new System.Uri(url);
            var urlParameters = System.Web.HttpUtility.ParseQueryString(uri.Query);
            foreach (var parameter in urlParameters.AllKeys)
            {
                if (isPost)
                    allParameters.Add(new Dimebrain.TweetSharp.Core.Web.HttpPostParameter(parameter, urlParameters[parameter]));
                else
                    allParameters.Add(parameter, urlParameters[parameter]);
            }

            var signatureBase = OAuthTools.ConcatenateRequestElements(isPost
                ? Dimebrain.TweetSharp.Core.Web.WebMethod.Post
                : Dimebrain.TweetSharp.Core.Web.WebMethod.Get, url, allParameters);
            var signature = OAuthTools.GetSignature(Dimebrain.TweetSharp.Core.OAuth.OAuthSignatureMethod.HmacSha1,
                signatureBase, ConsumerSecret, account.AuthTokenSecret);

            authParameters.Insert(authParameters.Count - 1, new WebParameter("oauth_signature", signature));

            var sb = new System.Text.StringBuilder();
            sb.Append("OAuth ");
            foreach (var p in authParameters)
                sb.Append(p.Name + "=\"" + p.Value + "\",");
            return sb.ToString(0, sb.Length - 1); // Leave the last ","
        }


        public static Account TryGetAccount(System.Web.HttpRequest request)
        {
            var requestToken = request["oauth_token"];
            if (requestToken == null)
                return null;

            try
            {
                // exchange returned request token for access token
                var access = GetAccessToken(requestToken);
                if (access.Token == null || access.TokenSecret == null)
                    throw new System.Exception("Null token");

                // make an oauth-authenticated call with the access token,
                // and remember you need to persist this token for this user's auth
                var query = FluentTwitter.CreateRequest()
                    .AuthenticateWith(ConsumerKey,
                                      ConsumerSecret,
                                      access.Token,
                                      access.TokenSecret)
                    .Account()
                    .VerifyCredentials()
                    .AsJson();

                // OAuth is still in Beta, YMMV
                var response = query.Request();

                var identity = response.AsUser();
                if (identity != null)
                {
                    var newAccount = new Account
                    {
                        UserName = identity.ScreenName,
                        AuthToken = access.Token,
                        AuthTokenSecret = access.TokenSecret,
                        ApplicationConsumerKey = ConsumerKey,
                    };
                    KlearStorage.Persister.Instance.Insert(newAccount);
                    return newAccount;
                }
                //else
                {
                    var error = response.AsError();
                    if (error != null)
                        Log.LogError("TryGetAccount Twitter Error: " + error.ErrorMessage);
                    else
                        Log.LogError("TryGetAccount failed to extract the account from: " + response);
                    return null;
                }
            }
            catch (System.Exception ex)
            {
                Log.LogError("Invalid token: " + requestToken + "\r\nException: " + ex);
                return null;
            }
        }


        private static Dimebrain.TweetSharp.Model.OAuthToken GetAccessToken(string requestToken)
        {
            var accessToken = FluentTwitter.CreateRequest()
                .Authentication.GetAccessToken(ConsumerKey, ConsumerSecret, requestToken);

            var response = accessToken.Request();
            var result = response.AsToken();

            if (result == null)
            {
                var error = response.AsError();
                if (error != null)
                    throw new System.Exception(error.ErrorMessage);
            }

            return result;
        }
    }
}